﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace EasyMvvm
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class DesignFunctionAttribute : Attribute
    {
        private static object locker = new object();
        static DesignFunctionAttribute()
        {
            RunDesignTimeFunctions();
        }

        static bool HasRun = false;

        internal static void RunDesignTimeFunctions()
        {
            AssemblyName currentAssemblyName;
            AssemblyName[] referencedAssemblies;
            Assembly[] assembiles;
            bool nonStaticMethodFound;
            List<MethodInfo> methodsToRun;
            MethodInfo[] methods;
            Type[] types;
            object instance = null;
            bool hasReference;

            lock (locker)
            {
                if (!Designer.IsDesignMode || HasRun)
                    return;

                HasRun = true;
            }

            currentAssemblyName = Assembly.GetExecutingAssembly().GetName();
            assembiles = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly assembly in assembiles)
            {
                //Check the assembly first if it has a reference to this assembly.
                hasReference = false;
                referencedAssemblies = assembly.GetReferencedAssemblies();
                foreach (AssemblyName reference in referencedAssemblies)
                {
                    if (reference.FullName == currentAssemblyName.FullName)
                    {
                        hasReference = true;
                        break;
                    }

                }

                if (!hasReference)
                    continue;

                //Loop all public classes and loop all public methods.
                types = assembly.GetTypes();
                foreach (Type type in types)
                {
                    nonStaticMethodFound = false;
                    if (type.IsPublic && !type.IsAbstract && !type.IsInterface)
                    {
                        methodsToRun = new List<MethodInfo>();
                        methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
                        foreach (MethodInfo method in methods)
                        {
                            if (method.IsPublic && method.GetParameters().Length == 0 && method.GetCustomAttributes(typeof(DesignFunctionAttribute), true).Length > 0)
                            {
                                if (!method.IsStatic)
                                    nonStaticMethodFound = true;

                                methodsToRun.Add(method);
                            }
                        }

                        if (methodsToRun.Count > 0)
                        {
                            if (nonStaticMethodFound)
                                instance = Activator.CreateInstance(type);

                            foreach (MethodInfo method in methodsToRun)
                            {
                                if (method.IsStatic)
                                    method.Invoke(null, null);
                                else if (instance != null)
                                    method.Invoke(instance, null);
                            }
                            //If the type was instanced and it implements IDisposable dispose it!
                            if (nonStaticMethodFound && type.GetInterfaces().Contains(typeof(IDisposable)))
                                ((IDisposable)instance).Dispose();


                        }
                    }
                }
            }
        }
    }
}
